package club.westudy.distributed.lock.internal.redis.client;

import org.redisson.api.RFuture;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.function.BiConsumer;

/**
 * Redis锁，基于setNx，同时利用Client开启的定时线程进行检查锁的自动续租
 */
public class RedisLock implements RLock {
    public static final String TRY_LOCK_SCRIPT = "if (redis.call('exists', KEYS[1]) == 0) then " +
            "redis.call('hset', KEYS[1], ARGV[2], 1); " +
            "redis.call('pexpire', KEYS[1], ARGV[1]); " +
            "return nil; " +
            "end; " +
            "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
            "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
            "redis.call('pexpire', KEYS[1], ARGV[1]); " +
            "return nil; " +
            "end; " +
            "return redis.call('pttl', KEYS[1]);";
    /**
     * 加锁名称
     */
    String name;
    RedisClient redisClient;

    public RedisLock(String name, RedisClient redisClient) {
        this.name = name;
        this.redisClient = redisClient;
    }

    @Override
    public void lock() {
        try {
            lock(-1, null, false);
        } catch (InterruptedException e) {
            throw new IllegalStateException();
        }
    }


    @Override
    public void lock(long leaseTime, TimeUnit unit) {
        try {
            lock(leaseTime, unit, false);
        } catch (InterruptedException e) {
            throw new IllegalStateException();
        }
    }

    @Override
    public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
        lock(leaseTime, unit, true);
    }


    void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
        //TODO
        long threadId = Thread.currentThread().getId();
        // 1.获取当前加锁名称对应在redis数据库创建加锁记录，如果返回null，则直接反回，表示加锁成功
        Long ttl = tryAcquire(leaseTime, unit, threadId);
        if (ttl == null) {
            return;
        }
        // 2.如果返回ttl说明已经加锁，自旋等待再次申请加锁
        try {
            while (true){
                ttl = tryAcquire(leaseTime, unit, threadId);
                // 加锁成功，结束
                if (ttl == null) {
                    break;
                }
                if (ttl >= 0) {
                    //TODO
                }else{
                    //TODO
                }
            }
        }finally {

        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        lock(-1, null, true);
    }

    @Override
    public boolean tryLock() {
        return get(tryLockAsync());
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return tryLock(time, -1, unit);
    }


    @Override
    public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
        //TODO
        return false;
    }

    @Override
    public void unlock() {

    }

    @Override
    public Condition newCondition() {
        throw new UnsupportedOperationException("RedisLock is unsupported!");
    }

    @Override
    public RFuture<Boolean> tryLockAsync() {
        return tryLockAsync(Thread.currentThread().getId());
    }

    @Override
    public RFuture<Boolean> tryLockAsync(long threadId) {
        return tryAcquireOnceAsync(-1, null, threadId);
    }

    @Override
    public RFuture<Void> unlockAsync() {
        return unlockAsync(Thread.currentThread().getId());
    }

    @Override
    public RFuture<Void> unlockAsync(long threadId) {
        //TODO
        return null;
    }

    RFuture<Boolean> tryAcquireOnceAsync(long leaseTime, TimeUnit unit, long threadId) {
        //TODO
        return null;
    }

    RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {
        if (leaseTime != -1){
            return tryLockInnerAsync(leaseTime, unit, threadId);
        }
        RFuture<Long> future = tryLockInnerAsync(1L, unit, threadId);
        future.onComplete((ttlRemaining, e) -> {
            if (e != null) {
                return;
            }

            // lock acquired
            if (ttlRemaining == null) {
                //scheduleExpirationRenewal(threadId);
                // TODO 触发续租
            }
        });
        //TODO
        return future;
    }
    RFuture<Long> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId) {
        //持有锁时间
        long leaseTime0 = unit.toMillis(leaseTime);
        // TODO 执行脚本TRY_LOCK_SCRIPT;
        redisClient.eval(TRY_LOCK_SCRIPT, Arrays.asList(name), Arrays.asList(Long.toString(leaseTime0), redisClient.getClientId()));
        return null;
    }
    Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) {
        //通过redis进行加锁，加锁成功返回null，否则返回已存在锁的ttl时间
        return get(tryAcquireAsync(leaseTime, unit, threadId));
    }

    /**
     * 调用该方法将发生阻塞
     * @param future
     * @param <V>
     * @return
     */
    <V> V get(RFuture<V> future) {
        //
        try {
            return future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return (V) Boolean.FALSE;
    }
}
